package tota.middleware;

/**
 * <p>Title: TOTA</p>
 * <p>Description: TOTA</p>
 * <p>Copyright: Copyright (c) 2002</p>
 * <p>Company: DII - DISMI</p>
 * @author Marco Mamei
 * @version 1.0
 */

import java.util.*;

import emulator.utils.*;
import emulator.peers.*;

import eventengine.middleware.*;
import lime.middleware.*;
//import limefast.middleware.*;
import radarmiddleware.middleware.*;
import gpsmiddleware.middleware.*;

import tuplespace.tuples.*;
import corepeerservices.tuples.*;
import tota.tuples.*;

public class TotaMiddleware implements TotaInterface, ReactiveComponent
{
 static final boolean CLOCK_ON = true;
 static final boolean RADAR_ON = true;
 static final boolean GPS_ON = true;

 /* This is the main socket used for lime communication */

 /* tota actually relies on the core socket */
 static final String TOTA_SOCKET = "core";
 //static final String TOTA_ADDRESS = "239.1.2.4";
 //static final int TOTA_PORT = 9001;

 private PeerInterface_ApplicationSide peer;
 private Radar radar;
 private GPS gps;
 private LimeInterface lime;
 private DiffusionBuffer diffusionBuffer;

 /* TOTA accepts subscriptions relative to: network events and tuples being
 inserted in the tuple space or in the neighbors' tuple spaces */
 private EventInterface tsEvent;
 private EventInterface vtsEvent;
 private EventInterface sensorEvent;

 private int tupleCounter = 0;

 /* sensor tuples pre-built */
 private SensorTuple internalClock;
 private SensorTuple radarT;
 private SensorTuple gpsT;

 public TotaMiddleware(PeerInterface_ApplicationSide peer)
 {
  this.peer = peer;
  this.diffusionBuffer = new DiffusionBuffer(peer,TOTA_SOCKET);
  radar = new RadarMiddleware(peer);
  gps = new GPSMiddleware(peer);

  lime = new Lime(peer);
  //lime = new LimeFast(peer);

  tsEvent = new EventServer("TOTA"+peer.toString()+"TS");
  vtsEvent = new EventServer("TOTA"+peer.toString()+"VTS");
  sensorEvent = new EventServer("TOTA"+peer.toString()+"SENSOR");

  lime.setOutsideEvent(tsEvent, vtsEvent);

  /* create TOTA communication socket */
  //peer.createSocketMulticast(TOTA_SOCKET,TOTA_ADDRESS,TOTA_PORT);
  /* set up call back reading */
  peer.callBackReading(TOTA_SOCKET,"<TOTA>",this,"TOTA");

  /* create the clock tuple here just for optimization. It is faster to update
     the content of an already created tuple rather than create one from skratch
     at every time step.
  */
  internalClock = new SensorTuple("<sensor=clock><value=*>");
  radarT = new SensorTuple("<sensor=radar><value=*>");
  gpsT = new SensorTuple("<sensor=gps><value=*>");
 }

 /*****************************************************************************/
 /*                        TOTA PRIVATE METHODS                               */
 /*****************************************************************************/
 private String genUniqueId()
 {
  tupleCounter ++;
  return new String(peer.toString()+"-tota"+tupleCounter);
 }


 /*****************************************************************************/
 /*                 METHODS THAT ARE PART OF THE MODEL                        */
 /*****************************************************************************/
 public void inject (TotaTuple tuple)
 {
  tuple.init(this);
  tuple.id = genUniqueId();
  tuple.execute();
 }
 public Vector read (Tuple template)
 {
  Vector v = lime.read(template);
  return v;
 }
 public Vector readOneHop(Tuple template)
 {
  Vector v = lime.rdOneHop(template);
  return v;
 }
 public Tuple keyrd (Tuple template)
 {
  Tuple t = lime.keyrd(template);
  return t;
 }
 public Vector keyrdOneHop (Tuple template)
 {
  Vector v = lime.keyrdOneHop(template);
  return v;
 }
 public void subscribe (Tuple template, ReactiveComponent comp, String reaction)
 {
  if (template instanceof SensorTuple)
   sensorEvent.subscribe(template,comp,reaction);
  else
  {
   tsEvent.subscribe(template,comp,reaction);
   vtsEvent.subscribe(template,comp,reaction);
  }
 }
 public void unsubscribe (Tuple template, ReactiveComponent comp)
 {
  if (template instanceof SensorTuple)
   sensorEvent.unsubscribe(template,comp);
  else
  {
   tsEvent.unsubscribe(template,comp);
   vtsEvent.unsubscribe(template,comp);
  }
 }

 /* a store method is required to be used by tuples */
 public void store (Tuple t)
 {
  lime.write(t);
 }
 /* this method is used toremove a tuple form tthe TOTA local tuple space */
 public void delete (Tuple t)
 {
  Tuple all = new Tuple("<content=*>");
  sensorEvent.unsubscribe(all,(ReactiveComponent)t);
  tsEvent.unsubscribe(all,(ReactiveComponent)t);
  vtsEvent.unsubscribe(all,(ReactiveComponent)t);
  lime.keyextr(t);
  this.setColor(0,255,0);
 }
 /* These methods can be used by a TOTA tuple (or eventually in further
 versions by a mobile agent) to move to all the neighbor nodes.

 Actually, accordingly to the diffusion - rather than flooding - appraoch tuples
 are not broadcasted now. They are stored in a special buffer and they are boradcasted
 only at the next boradcast period. Before being broadcasted tuples can be conveniently
 filtered to reduce bandwidth.... */
 public void move(TotaTuple t)
 {
  diffusionBuffer.store(t);
 }

 /*****************************************************************************/
 /*                            REACTION METHODS                               */
 /*****************************************************************************/
 public void react(String reaction, String event)
 {
  if(reaction.equals("TOTA"))
  {
   /* receives something like:
   <TOTA><[tota.tuples.structure.FloodTuple][<from=P81><id=P81-1><content=marco><drives=*>]>
   */
   String[] data = StaticUtilities.splitMessage(event,'<','>');
   TotaTuple t = (TotaTuple)Tuple.deserialize(data[1]);
   
   System.out.println(this.peer.getAddress()+" received a tuple from "+ t.from);
   
   t.init(this);
   t.execute();
  }
 }

 /*****************************************************************************/
 /*             METHODS THAT ARE NOT PART OF THE MODEL                        */
 /*****************************************************************************/

 /* this method is a commodity method that returns true if the tuple is present
 in the TOTA tuple space */
 public boolean contains (TotaTuple t)
 {
  return true;
 }
 /* The methods are mainly for debugging purposes */
 public String toString()
 {
  return peer.toString();
 }
 public String getAddress()
 {
  return peer.getAddress();
 }
 /* This is connected to the peer method with the same name */
 public void setColor(int r, int g, int b)
 {
  peer.setColor(r,g,b);
 }

 /*****************************************************************************/
 /*                             ACTIVE METHOD                                 */
 /*****************************************************************************/
 public void step(int time)
 {
  /* here the diffusion buffer is flushed on the netowrk, by broadcasting the
  relative tuples */
  diffusionBuffer.fire();
  lime.step(time);

  if(CLOCK_ON)
  {
   /* manage internal clock */
   internalClock.value = String.valueOf(time);
   lime.write(internalClock);
   sensorEvent.generateEvent(internalClock);
  }

  if(RADAR_ON)
  {
   /* manage radar device */
   String newRead = radar.scan();
   if(newRead!=null && !radarT.value.equals(newRead))
   {
    radarT.value = newRead;
    lime.write(radarT);
    sensorEvent.generateEvent(radarT);
   }
  }

  if(GPS_ON)
  {
   /* manage radar device */
   String newRead = gps.getPos();
   if(newRead!=null && !gpsT.value.equals(newRead))
   {
    gpsT.value = newRead;
    lime.write(gpsT);
    sensorEvent.generateEvent(gpsT);
   }
  }
 }
}


/* this class is used to implement the concept of information diffusion rather
than information floding:

In the flooding mechanism, each individual node periodically broadcasts (pushes)
information about itself. Whenever a node receives a broadcast message, it stores
it and immediately forwards it by rebroadcasting the message. Obviously, this method
is not scalable, due to messages flooding over the network, especially in high density
networks.

In the diffusion mechanism  each node broadcasts information about itself and the
other nodes it knows about. Whenever a node receives broadcast information, it updates
its stored information and defers forwarding the information to the next broadcast
period, at which time it broadcasts its updated information. The diffusion mechanism
is scalable, since the number of broadcast messages is limited and they do not flood
the network. */

class DiffusionBuffer
{
 private PeerInterface_ApplicationSide peer;
 String SOCKET;
 private HashMap buff;

 private Iterator iter;

 DiffusionBuffer(PeerInterface_ApplicationSide peer, String SOCKET)
 {
  this.peer = peer;
  this.SOCKET = SOCKET;
  buff = new HashMap();
 }

 public void store(TotaTuple t)
 {
  buff.put(t.id,t);
 }

 public void fire()
 {
  if(buff.size() > 0)
  {
   iter = buff.values().iterator();
   while(iter.hasNext())
    peer.send(SOCKET,"<TOTA><"+((TotaTuple)iter.next()).serialize()+">");
  }
  buff.clear();
 }
}